//
// Created by denglibin on 2021/5/18.
//
#include "image_capture.h"
AVCodecContext* vDecodeContext = NULL; //视频解码器

/**
 * 音视频采集设备
 * 命令： .\ffmpeg -list_devices true -f dshow -i dummy  (ffmpeg在安装目录的bin目录下)
 */
static void dshow_device(){
    AVFormatContext * avFormatContext = avformat_alloc_context();
    AVDictionary * opt = NULL;
    av_dict_set(&opt, "list_devices", "true", 0);
    AVInputFormat * inputFormat = av_find_input_format("dshow");
    avformat_open_input(&avFormatContext, "video=dummy", inputFormat, &opt);

}
/**
 * 获取指定视频采集设备支持的分辨率、帧率和像素格式等属性
 * 命令: .\ffmpeg -list_options true -f dshow -i video="USB2.0 VGA UVC WebCam"
 */
static void dshow_option(){
    AVFormatContext * avFormatContext = avformat_alloc_context();
    AVDictionary * opt = NULL;
    av_dict_set(&opt, "list_options", "true", 0);
    AVInputFormat * inputFormat = av_find_input_format("dshow");
    //USB2.0 VGA UVC WebCam 视频采集设备 通过上面的函数查看得知
    avformat_open_input(&avFormatContext, "video=USB2.0 VGA UVC WebCam", inputFormat, &opt);
}

/**
 * 初始化视频输入流（摄像头）
 */
static int initVideoInputStream(AVFormatContext **vFormatContext,AVInputFormat *inputFormat, AVDictionary** v_options){
    int res;

    char* video_device = "video=USB2.0 VGA UVC WebCam";//视频采集设备
    av_dict_set_int(v_options, "rtbufsize", 3041280 * 100, 0);
    //打开流
    res= avformat_open_input(vFormatContext, video_device, inputFormat, v_options);
    if(res != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "视频-视频设备打开失败");
        return -1;
    }

    //寻找流信息
    res = avformat_find_stream_info(*vFormatContext, NULL);
    if(res !=0 ){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "视频-查找流信息失败");
        return -1;
    }
    //查找视频流
    int videoStream = av_find_best_stream(*vFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

    if(videoStream == AVERROR_STREAM_NOT_FOUND){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "视频-获取视频流失败");
        return -1;
    }

    //找解码器
    AVCodec* vCodec = avcodec_find_decoder((*vFormatContext)->streams[videoStream]->codecpar->codec_id);
    if(!vCodec){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "视频-查找解码器失败");
        return -1;
    }

    vDecodeContext = avcodec_alloc_context3(vCodec);
    avcodec_parameters_to_context(vDecodeContext, (*vFormatContext)->streams[videoStream]->codecpar);

    //打开解码器
    res = avcodec_open2(vDecodeContext, vCodec,  NULL);
    if(res != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "视频-打开解码器失败");
        return -1;
    }

    return videoStream;

}
/**
 * 初始化音频输入流（麦克风）
 * @param avFormatContext
 * @param options
 */
static int initAudioInputStream(AVFormatContext **aFormatContext, AVInputFormat *inputFormat, AVDictionary** a_options){
    char* audio_device = "audio=麦克风 (Realtek High Definition Audio)"; //音频采集设备

    //打开流
    int res = avformat_open_input(aFormatContext, audio_device, inputFormat, a_options);
    if(res != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "音频-音频设备打开失败");
        return -1;
    }

    //寻找流信息
    res = avformat_find_stream_info(*aFormatContext, NULL);
    if(res !=0 ){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "音频-查找流信息失败");
        return -1;
    }
    //查找视频流
    int audioStream = av_find_best_stream(*aFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

    if(audioStream == AVERROR_STREAM_NOT_FOUND){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "音频-获取视频流失败");
        return -1;
    }

    //找解码器
    AVCodec* aCodec = avcodec_find_decoder((*aFormatContext)->streams[audioStream]->codecpar->codec_id);
    if(!aCodec){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "音频-查找解码器失败");
        return -1;
    }

    AVCodecContext* aCodecContext = avcodec_alloc_context3(aCodec);
    avcodec_parameters_to_context(aCodecContext, (*aFormatContext)->streams[audioStream]->codecpar);

    //打开解码器
    res = avcodec_open2(aCodecContext, aCodec,  NULL);
    if(res != 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "音频-打开解码器失败");
        return -1;
    }
    return audioStream;

}

/**
 * 初始化视频编码器
 * @return
 */
static AVCodecContext* initVideoEncoder(){
    //视频编码器
    //查找H264编码器
    AVCodec*  mpeg4Codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
    if(!mpeg4Codec){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "H264编码器查找失败");
        exit(-1);
    }
    AVCodecContext* mpeg4CodecCtx = avcodec_alloc_context3(mpeg4Codec);
    mpeg4CodecCtx->codec_id = AV_CODEC_ID_MPEG4;
    mpeg4CodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    mpeg4CodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    mpeg4CodecCtx->width = vDecodeContext->width;
    mpeg4CodecCtx->height = vDecodeContext->height;
    mpeg4CodecCtx->time_base.num = 1;
    mpeg4CodecCtx->time_base.den = 25;	//帧率（即一秒钟多少张图片）
    mpeg4CodecCtx->bit_rate = 400000;	//比特率（调节这个大小可以改变编码后视频的质量）
    mpeg4CodecCtx->gop_size = 250;
    mpeg4CodecCtx->qmin = 10;
    mpeg4CodecCtx->qmax = 51;
    //if (vCodecContext->flags & AVFMT_GLOBALHEADER)
    {
        mpeg4CodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    //打开编码器
    int ret = avcodec_open2(mpeg4CodecCtx, mpeg4Codec, NULL);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "打开视频编码器失败");
        return NULL;
    }
    return mpeg4CodecCtx;
}
/**
 * 图像采集
 * 命令:.\ffmpeg -f dshow -i video="USB2.0 VGA UVC WebCam" -f dshow -i audio="麦克风 (Realtek High Definition Audio)"  -acodec aac -strict -2 mycamera.mkv
 * USB2.0 VGA UVC WebCam：视频采集设备名称
 * 麦克风 (Realtek High Definition Audio): 音频采集设备名称
 */
void imageCapture(){
    //网络
    avformat_network_init();
    //设备
    avdevice_register_all();

    //在控制台查看音视频采集设备
    //dshow_device();
    //dshow_option();

    AVInputFormat *inputFormat  = av_find_input_format("dshow");
    if(inputFormat == NULL){
        av_log(NULL, AV_LOG_ERROR, "av_find_input_format 错误");
        exit(-1);
    }
    AVFormatContext *vFormatContext = NULL;

    AVDictionary* v_options = NULL;
    //av_dict_set_int(&v_options, "rtbufsize", 18432000  , 0);

    //视频输入流
    int v = initVideoInputStream(&vFormatContext, inputFormat, &v_options);
    AVStream * vStream = vFormatContext->streams[v];
    AVRational vRational = av_guess_frame_rate(vFormatContext, vStream, NULL); //帧率
    av_log(NULL, AV_LOG_INFO, "帧率:%d/%d = %d\n", vRational.num, vRational.den, vRational.num/vRational.den);
    int frame_rate = vRational.num/vRational.den;

    AVFormatContext *aFormatContext = NULL;
    AVDictionary* a_options = NULL;
    //音频输入流
   // int a = initAudioInputStream(&aFormatContext, inputFormat, &a_options);
   // AVStream * aStream = vFormatContext->streams[a];


    //视频编码器
    AVCodecContext* mpeg4CodecCtx = initVideoEncoder();

    //输出
    AVFormatContext* outputFormatContext = NULL;
    char* fileName = "Camera.mp4";
    int ret = avformat_alloc_output_context2(&outputFormatContext, NULL, "mp4", fileName);
    if(ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "打开output_context失败");
        return;
    }
    //创建输出流
    AVStream * out_stream = avformat_new_stream(outputFormatContext, NULL);
    if(!out_stream){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "avformat_new_stream失败");
        return;
    }
    //用指定的编码器填充流的参数信息
    ret = avcodec_parameters_from_context(out_stream->codecpar, mpeg4CodecCtx);

    if (ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "avcodec_parameters_from_context失败");
        return;
    }
    //打印输出流信息
    av_dump_format(outputFormatContext, 0, fileName, 1);

    if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE))
    {
        // 2.3 创建并初始化一个AVIOContext, 用以访问URL（outFilename）指定的资源
        ret = avio_open(&outputFormatContext->pb, fileName, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            printf("can't open output URL: %s\n", fileName);
            return;
        }
    }

    //数据处理
    //写输出文件
    ret = avformat_write_header(outputFormatContext, NULL);
    if (ret < 0){
        av_log(NULL, AV_LOG_ERROR, "%s\n", "avformat_write_header失败");
        return;
    }

    //分配帧空间 存放解码后数据 存储非压缩的数据（视频对应RGB/YUV像素数据，音频对应PCM采样数据）
    AVFrame * pFrame = av_frame_alloc(); //转换前
    //分配帧空间
    AVFrame * pFrameYUV = av_frame_alloc(); //转换后

    //分配空间 存放编码后的数据 （存储压缩数据（视频对应H.264等码流数据，音频对应AAC/MP3等码流数据））
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    unsigned char* outBuffer = (unsigned char*) av_malloc(
            av_image_get_buffer_size(mpeg4CodecCtx->pix_fmt, mpeg4CodecCtx->width,
                                     mpeg4CodecCtx->height, 1));

    //填充数据
    av_image_fill_arrays(pFrameYUV->data,   //目标
                         pFrameYUV->linesize,
                         outBuffer, //源
                         mpeg4CodecCtx->pix_fmt,
                         mpeg4CodecCtx->width,
                         mpeg4CodecCtx->height,
                         1);

    struct SwsContext* pImgConvertCtx = sws_getContext(vDecodeContext->width, vDecodeContext->height,vDecodeContext->pix_fmt, //源
                                                       mpeg4CodecCtx->width,mpeg4CodecCtx->height,mpeg4CodecCtx->pix_fmt, //目标
                                                       SWS_BICUBIC,
                                    NULL, NULL, NULL);
    int frameIndex = 0;

    //读取数据
    while (frameIndex < 200) //读1000帧
    {
        // 从输入流读取一帧数据到packet
        ret = av_read_frame(vFormatContext, packet);
        if (ret < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "%s\n", "av_read_frame失败");
            break;
        }
        //视频流
        if (packet->stream_index == AVMEDIA_TYPE_VIDEO)
        {
            //解码
            //解码一帧压缩数据->得到视频像素数据->yuv格式
            //发送一帧视频压缩数据,放到解码队列中
            ret = avcodec_send_packet(vDecodeContext, packet);
            if(ret != 0){
                av_log(NULL, AV_LOG_ERROR, "%s\n", "[ERROR]=>avcodec_send_packet error!");
                break;
            }
            //解码一帧视频压缩数据,将成功的解码队列中取出1个frame
            if (avcodec_receive_frame(vDecodeContext, pFrame) >= 0)
            {
                //转换
                sws_scale(pImgConvertCtx,
                          (const unsigned char* const*) pFrame->data,pFrame->linesize, 0, vDecodeContext->height, //源
                          pFrameYUV->data,pFrameYUV->linesize); //目标


                pFrameYUV->format = mpeg4CodecCtx->pix_fmt;
                pFrameYUV->width = mpeg4CodecCtx->width;
                pFrameYUV->height = mpeg4CodecCtx->height;

                //编码
                ret = avcodec_send_frame(mpeg4CodecCtx, pFrameYUV);
                if (ret < 0)
                {
                    av_log(NULL, AV_LOG_ERROR, "%s\n", "297行:avcodec_send_frame失败");
                    break;
                }
                //接收编码后的数据
                if (avcodec_receive_packet(mpeg4CodecCtx, packet) >= 0)
                {
                    // 设置输出DTS,PTS
                    packet->pts = packet->dts = frameIndex * (out_stream->time_base.den) /out_stream->time_base.num / frame_rate;
                    frameIndex++;
                    //写出编码好的一帧数据到输出流
                    ret = av_interleaved_write_frame(outputFormatContext, packet);
                    if (ret < 0)
                    {
                        av_log(NULL, AV_LOG_ERROR, "%s\n", "310行:av_interleaved_write_frame失败");
                        break;
                    }
                }
            }
        }
        //经过av_paceket_unref函数之后，将data置为NULL,size置为0
        av_packet_unref(packet);
    }
    av_log(NULL, AV_LOG_INFO, "读取帧数:%d\n", frameIndex);
    av_write_trailer(outputFormatContext);
    avformat_close_input(&vFormatContext);
    avformat_close_input(&aFormatContext);

    /* close output */
    if (outputFormatContext && !(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&outputFormatContext->pb);
    }
    avformat_free_context(outputFormatContext);

}